package org.dg.iag.dao;

import java.io.IOException;

import org.apache.log4j.Logger;
import org.dg.InitialServer;
import org.dg.iag.entity.User;
import org.dg.iag.query.XQueryGet;
import org.dg.iag.query.XQueryPost;
import org.dg.iag.util.ApplicationConstants;
import org.dg.iag.util.ObjectGetter;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.ext.xstream.XstreamRepresentation;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.ResourceException;

import com.thoughtworks.xstream.XStream;

/**
 * 
 * Generic data access object for accessing elements from an XML file in a the XML database.
 * 
 * @author Alex Gartner
 *
 * @param <T> Main object that we're trying to access, modify, save, delete from the db
 * @param <K> Type of object that wraps a list of Ts
 */
public class GenericDAO<T,K> {
	
	private static Logger logger = Logger.getLogger(GenericDAO.class);
	
	private String entireXMLPath;
	XStream xstream 	= null;
	
	/**
	 * 
	 * @param pathToXml Path to xml file in the db after the "/db/[application_folder]/" part 
	 */
	public GenericDAO(String pathToXml) {
		xstream		= new XstreamRepresentation<User>(new User("test", "test")).getXstream();
		this.entireXMLPath	= ApplicationConstants.APP_REST_PATH + pathToXml;
	}
	
	/**
	 * 
	 * @param xqg make sure that the paths specified in the xqg object is the part after the "/db/[application_folder]/pathToXML"
	 * @return
	 */
	public String getElementsAsString(XQueryGet xqg) {
		return this.getElementsAsString(this.entireXMLPath + xqg.toString() ) ;
	}
	
	public String getElementsAsString() {
		return this.getElementsAsString(this.entireXMLPath);
	}
	
	public String getElementsAsString(String url) {
		ClientResource cr = new ClientResource(InitialServer.configureContext(true), url);
		cr.setProtocol(Protocol.HTTP);
		cr.setMethod(Method.GET);
		cr.setChallengeResponse(ChallengeScheme.HTTP_BASIC, ApplicationConstants.EXIST_USER, ApplicationConstants.EXIST_PASSWORD);
		
		Representation result			= null;
		try {
			result		= cr.get(MediaType.APPLICATION_XML);
		}
		catch (ResourceException e) {
			e.printStackTrace();
			return null;
		}
		
		if ( result == null )
			return null;
		
		String resultStr;
		try {
			resultStr = result.getText();
		} catch (IOException e) {
			
			e.printStackTrace();
			return null;
		}
		
		cr.release();
		
		return resultStr;
	}
	
	
	public Representation getElementsAsRepresentation(MediaType mediaType) {
		return this.getElementsAsRepresentation(mediaType, this.entireXMLPath );
	}
	
	public Representation getElementsAsRepresentation(MediaType mediaType, String url) {
		ClientResource cr = new ClientResource(InitialServer.configureContext(true), url);
		cr.setProtocol(Protocol.HTTP);
		cr.setMethod(Method.GET);
		cr.setChallengeResponse(ChallengeScheme.HTTP_BASIC, ApplicationConstants.EXIST_USER, ApplicationConstants.EXIST_PASSWORD);
		
		Representation result			= null;
		
		try {
			if ( mediaType != null )
				result		= cr.get( mediaType );
			else 
				result		= cr.get();
		}
		catch (ResourceException e) {
			e.printStackTrace();
			return null;
		}
		
		cr.release();
		
		return result;
	}
	
	public K getElements(Class<? extends ObjectGetter<K>> wrapperClass) {
		return this.getElements(wrapperClass, null);
	}
	
	/**
	 * 
	 * @param wrapperClass
	 * @return root element from the XML specified in the constructor
	 */
	public K getElements(Class<? extends ObjectGetter<K>> wrapperClass, XQueryGet xqg) {
		String url			= (xqg==null)?this.entireXMLPath:this.entireXMLPath+xqg;
		ClientResource cr 	= new ClientResource(InitialServer.configureContext(true), url );
		cr.setProtocol(Protocol.HTTP);
		cr.setMethod(Method.GET);
		cr.setChallengeResponse(ChallengeScheme.HTTP_BASIC, ApplicationConstants.EXIST_USER, ApplicationConstants.EXIST_PASSWORD);
		
		ObjectGetter<K> wrapper		= cr.wrap(wrapperClass);
		K element					= wrapper.getElement();
		
		cr.release();
		
		return element;
	}
	public T getElement(Class<? extends ObjectGetter<T>> wrapperClass, String xpath) {
		XQueryGet xqg	= new XQueryGet(this.entireXMLPath,xpath, false);
		
		ClientResource cr = new ClientResource( InitialServer.configureContext(true), xqg.toString() );
		cr.setProtocol(Protocol.HTTP);
		cr.setMethod(Method.GET);
		cr.setChallengeResponse(ChallengeScheme.HTTP_BASIC, ApplicationConstants.EXIST_USER, ApplicationConstants.EXIST_PASSWORD);

		
		ObjectGetter<T> wrapper	= cr.wrap(wrapperClass);
		T element					= wrapper.getElement();
		cr.release();
		return element;
	}
	/**
	 * 
	 * @param u
	 * @param xpath xpath pointing to where the element needs to be inserted
	 */
	public void addElement(T u, String xpath) {
		try {
			String elXml	= xstream.toXML(u);
			
			XQueryPost qp	= new XQueryPost();
			qp.setXmlns("http://exist.sourceforge.net/NS/exist");
			qp.setProperties(null);
			qp.setText("update insert " + elXml + " into " + xpath);
			
			ClientResource cr = new ClientResource( InitialServer.configureContext(true), this.entireXMLPath);
			cr.setProtocol(Protocol.HTTP);
			cr.setMethod(Method.POST);
			cr.setChallengeResponse(ChallengeScheme.HTTP_BASIC, ApplicationConstants.EXIST_USER, ApplicationConstants.EXIST_PASSWORD);
			//String qpXml			= xstream.toXML(qp);
			Representation qpRep	= new XstreamRepresentation<XQueryPost>(MediaType.APPLICATION_XML, qp);
			Representation result   = null;
			try {
				System.out.println("Before appending log");
				result	= cr.post(qpRep);
				result.exhaust();
				result.release();
				System.out.println("After appending log");
				cr.release();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				logger.error("ERROR::: after post");
				e.printStackTrace();
				if ( result != null) {
					result.exhaust();
					result.release();
				}
				if(cr!=null)
					cr.release();
			}
		}
		catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("ERROR::: from GenericDAO:addElement");
			e.printStackTrace();
		}

	}
	
	
	/**
	 * 
	 * @param u
	 * @param xpath xpath selecting which element in the xml needs to be modified
	 */
	public void modifyElement(T u, String xpath) {
		String elXml	= xstream.toXML(u);
		
		XQueryPost qp	= new XQueryPost();
		qp.setXmlns("http://exist.sourceforge.net/NS/exist");
		qp.setProperties(null);
		qp.setText("update replace " + xpath + " with " + elXml);
		
		ClientResource cr = new ClientResource(this.entireXMLPath);
		cr.setProtocol(Protocol.HTTP);
		cr.setMethod(Method.POST);
		cr.setChallengeResponse(ChallengeScheme.HTTP_BASIC, ApplicationConstants.EXIST_USER, ApplicationConstants.EXIST_PASSWORD);
		
		Representation qpRep	= new XstreamRepresentation<XQueryPost>(MediaType.TEXT_XML, qp);
		try {
			Representation result	= cr.post(qpRep);
			cr.release();
		} catch (ResourceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
}
